package com.contact.dao;

import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.joda.time.LocalDate;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.contact.util.Utils;
import com.contact.util.util.filters.DateRange;

public class HibBaseDAO<T> extends HibernateDaoSupport implements BaseDAO<T> {
    private transient final Class<T> beanClass;
    protected transient final String beanName;

    public HibBaseDAO(Class<T> beanClass) {
        super();
        this.beanClass = beanClass;
        this.beanName = beanClass.getSimpleName();
    }

    @Override
    public Class<T> getType() {
        return beanClass;
    }

    @Override
    public T getById(long id) {
        return getHibernateTemplate().get(beanClass, id);
    }

    @Override
    public List<T> getAll() {
        return getAllByCriteria(getCriteria(true), true);
    }

    @Override
    public List<T> getAllByAttribute(String attribute, Object value) {
        String[] attributes = { attribute };
        Object[] values = { value };
        return getAllByCriteria(createCriteria(attributes, values, true), true);
    }

    @Override
    public List<T> getAllByAttributes(String[] attributes, Object[] values) {
        return null;
    }

    @Override
    public List<T> getAllByOrAttributes(String[] attributes, Object[] values) {
        return null;
    }

    @Override
    public List<T> getAllByIds(Set<Long> ids) {
        return null;
    }

    @Override
    public T getByAttribute(String attribute, Object value) {
        return DataAccessUtils.uniqueResult(getAllByAttribute(attribute, value));
    }

    @Override
    public T getByAttributes(String[] attributes, Object[] values) {
        return DataAccessUtils.uniqueResult(getAllByAttributes(attributes, values));
    }

    @Override
    public T merge(T bean) {
        return super.getHibernateTemplate().merge(bean);
    }

    @Override
    public T persist(T bean) {
        super.getHibernateTemplate().saveOrUpdate(bean);
        return bean;
    }

    @Override
    public void saveAll(Collection<T> all) {
        super.getHibernateTemplate().saveOrUpdateAll(all);
    }

    @Override
    public void deleteById(long id) {

    }

    @Override
    public void deleteByIds(Set<Long> ids) {

    }

    @Override
    public void delete(T bean) {
        getHibernateTemplate().delete(bean);
    }

    @Override
    public void deleteAll(Collection<T> all) {

    }

    @Override
    public int getCountByAttributes(String[] attributes, Object[] values) {
        return 0;
    }

    private DetachedCriteria createCriteria(String[] attributes, Object[] values, boolean filterDeleted) {
        DetachedCriteria criteria = getCriteria(filterDeleted);
        for (int i = 0; i < attributes.length; i++) {
            Object value = values[i];
            String attr = attributes[i];
            criteria.add(getCriterion(attr, value));
        }
        return criteria;
    }

    protected DetachedCriteria getCriteria(boolean filterDeleted) {
        return DetachedCriteria.forClass(beanClass);
    }

    private static Criterion getCriterion(String attr, Object value) {
        Criterion criterion;
        Property prop = Property.forName(attr);

        if (value == null) {
            criterion = prop.isNull();
        } else if (value instanceof String && ((String) value).contains("%")) {
            criterion = prop.like(value);
        } else if (value instanceof DateRange) {
            criterion = getCriterion(prop, (DateRange) value);
        } else if (value instanceof Collection) {
            criterion = prop.in((Collection<?>) value);
        } else {
            criterion = prop.eq(value);
        }

        return criterion;
    }
    private static Criterion getCriterion(Property prop, DateRange value) {

        LocalDate fromDate = value.getFromDate();
        LocalDate toDate = value.getToDate();
        
        if(fromDate == null && toDate == null) {
            return null;
        }
        
        if(fromDate == null && toDate != null) {
            return prop.le(toDate);
        }
        
        if(fromDate != null && toDate == null) {
            return prop.ge(fromDate);
        }

        return prop.between(fromDate, toDate);
    }
    
    protected List<T> getAllByCriteria(DetachedCriteria criteria, boolean removeDuplicates) {
        if (removeDuplicates) {
            return Utils.removeDuplicates(getHibernateTemplate().findByCriteria(criteria));
        }
        return getHibernateTemplate().findByCriteria(criteria);
    }

    protected T getByCriteria(DetachedCriteria criteria) {
        return (T) DataAccessUtils.uniqueResult(getHibernateTemplate().findByCriteria(criteria));
    }

    protected int getCountByCriteria(DetachedCriteria criteria) {
        criteria.setProjection(Projections.rowCount());
        List<Long> longList = getHibernateTemplate().findByCriteria(criteria);
        if (Utils.isEmpty(longList)) {
            return 0;
        }
        return longList.get(0).intValue();
    }

    /*protected DetachedCriteria getCriteria(boolean filterDeleted) {
        return DetachedCriteria.forClass(beanClass);
    }*/
}
